// ICE_Implementation.h
// implementation of the CRM Ice objects

#if !defined(_ICE_Implementation_h_)
#define _ICE_Implementation_h_
#include "Integration.h"
#include <Ice/Ice.h>
#include <iostream>
#include <string>
#include <sstream>

#include <mysql.h>

class CRM_imp;

#define mysql_fast_query(m, q) \
	if(mysql_real_query(&m, q.str().c_str(), (int)q.str().length())) \
	{ \
		throw ::CustomerRelationsManagement::DBError(mysql_errno(&m), mysql_error(&m)); \
	} \


template <class T>
bool from_string(T& t, const std::string& s, std::ios_base&(*f)(std::ios_base&))
{
	std::istringstream iss(s);
	return !(iss >> f >> t).fail();
}

template<class T, typename Prx>
void make_proxy(T* imp, Prx &proxy, const ::Ice::Current& ice)
{
	proxy = Prx::uncheckedCast(ice.adapter->addWithUUID(IceUtil::Handle<T>(imp)));
}

std::string escape_string(std::string str);

class customer_imp : public CustomerRelationsManagement::customer
{

private:
	MYSQL &db_conn;
	int record_id;
	std::string name;

	customer_imp(MYSQL &db) : db_conn(db), record_id(0) { }
	customer_imp(MYSQL &db, int id) : db_conn(db), record_id(id) { db_load(); }
	friend class CRM_imp;

	void db_load()
	{
		std::ostringstream query;
		query << "SELECT name FROM customers WHERE customerid=" << record_id;
		MYSQL_RES *result;
		mysql_fast_query(db_conn, query);
		result = mysql_store_result(&db_conn);

		int nrows = (int)mysql_num_rows(result);

		if (nrows != 0)
		{
			MYSQL_ROW	row;
			row=mysql_fetch_row(result);
			name = row[0];
			mysql_free_result(result);
		}
		else
		{
			mysql_free_result(result);
			throw ::CustomerRelationsManagement::RecordNotFound(record_id, "customer_imp::db_load()");
		}
	}

public:

    virtual void save(const ::Ice::Current& = ::Ice::Current())
	{		
		std::cout << "Customer::Save()" << std::endl;
		std::ostringstream query;
		if (record_id != 0)
		{
			query << "UPDATE customers SET ";
		}
		else
		{
			query << "INSERT INTO customers SET ";
		}
		query << "name = '" << escape_string(name) << "'";
		

		if (record_id != 0)
		{
			query << "WHERE customerid=" << record_id;
			mysql_fast_query(db_conn, query);
		}
		else
		{
			mysql_fast_query(db_conn, query);
			record_id = (int)mysql_insert_id(&db_conn);
		}
	}

	virtual void _cpp_delete(const ::Ice::Current& = ::Ice::Current())
	{
		
		std::cout << "Customer::_cpp_delete()" << std::endl;
		if (record_id != 0)
		{
			std::ostringstream query;
			query << "DELETE FROM customers WHERE customerid=" << record_id;
			mysql_fast_query(db_conn, query);
		}
	}

    virtual ::Ice::Int getId(const ::Ice::Current& = ::Ice::Current())
	{
		
		std::cout << "Customer::getId()" << std::endl;
		return record_id;
	}


	virtual ::std::string getName(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Customer::getName()" << std::endl;
		return name;
	}

    virtual void setName(const ::std::string& aname, const ::Ice::Current& = ::Ice::Current())
	{
		
		std::cout << "Customer::setName()" << std::endl;
		name = aname;
	}


};

class order_imp : public CustomerRelationsManagement::order
{
private:
	MYSQL &db_conn;
	int record_id;
	int customer_id;
	std::string src;
	std::string dst;
	float weight;
	CustomerRelationsManagement::datetime deadline;
	CustomerRelationsManagement::OrderStatus status;

	order_imp(MYSQL &db) : db_conn(db), record_id(0), customer_id(0), weight(0.0), status(CustomerRelationsManagement::OPEN) { }
	order_imp(MYSQL &db, int id) : db_conn(db), record_id(id), customer_id(0), weight(0.0), status(CustomerRelationsManagement::OPEN) { db_load(); }
	friend class CRM_imp;

	void db_load()
	{
		std::ostringstream query;
		query << "SELECT customerid,src,dst,weight,deadline,status FROM orders WHERE orderid=" << record_id;
		MYSQL_RES *result;
		mysql_fast_query(db_conn, query);
		result = mysql_store_result(&db_conn);

		if (mysql_num_rows(result) != 0)
		{
			
			MYSQL_ROW	row;
			row=mysql_fetch_row(result);

			from_string<int>(customer_id, row[0], std::dec);	
			src = row[1];
			dst = row[2];			
			from_string<float>( weight, row[3], std::dec);
			// wee PARSE deadline = res[4]["deadline"];
			std::string stemp = row[4];
			stemp[4] = ' '; //YYYY-MM-DD
			stemp[7] = ' ';
			std::istringstream in_date(stemp);
			int itemp;
			in_date >> itemp;
			deadline.year = itemp;

			in_date >> itemp;
			deadline.month = itemp;

			in_date >> itemp;
			deadline.day = itemp;

			//sscanf(row[4], "%4d-%2d-%2d", &deadline.year, &deadline.month, &deadline.day);
			if (std::string(row[5]) == "DELIVERED")
				status = CustomerRelationsManagement::DELIVERED;
			else
				status = CustomerRelationsManagement::OPEN;

			mysql_free_result(result);
		}
		else
		{
			mysql_free_result(result);
			throw ::CustomerRelationsManagement::RecordNotFound(record_id, "order_imp::db_load()");
		}
	}

public:

    virtual void save(const ::Ice::Current& = ::Ice::Current())
	{
		
		std::cout << "Order::save()" << std::endl;
		std::ostringstream query;
		if (record_id != 0)
		{
			query << "UPDATE orders SET ";
		}
		else
		{
			query << "INSERT INTO orders SET ";
		}
		query << "customerid = " << customer_id;
		query << ", src = '" << escape_string(src) << "' ";
		query << ", dst = '" << escape_string(dst) << "' ";
		query << ", weight = " <<  weight;
		query << ", deadline = '" << deadline.year << "-" << int(deadline.month) << "-" << int(deadline.day)  << "' ";
		if (status == CustomerRelationsManagement::DELIVERED)
			query << ", status = 'DELIVERED' ";
		else
			query << ", status = 'OPEN' ";

		//mysql_fast_query(db_conn, query);
		if (record_id != 0)
		{
			query << "WHERE orderid=" << record_id;
			mysql_fast_query(db_conn, query);
		}
		else
		{
			std::string strtemp = query.str();
			mysql_fast_query(db_conn, query);
			record_id =  (int)mysql_insert_id(&db_conn);
			std::ostringstream query2;
			query2 << "INSERT INTO neworders SET orderid=" << record_id;
			mysql_fast_query(db_conn, query2);
		}
	}

    virtual void _cpp_delete(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::_cpp_delete()" << std::endl;
		if (record_id != 0)		{
			std::ostringstream query;
			query << "DELETE FROM orders WHERE orderid=" <<  record_id;
			mysql_fast_query(db_conn, query);
		}
	}

    virtual ::Ice::Int getId(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::getId()" << std::endl;
		return record_id;
	}

    virtual ::Ice::Int getCustomer(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::getCustomer()" << std::endl;
		return customer_id;
	}

    virtual void setCustomer(::Ice::Int id, const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::setCustomer()" << std::endl;
		customer_id = id;
	}

    virtual ::std::string getSrc(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::getSrc()" << std::endl;
		return src;
	}

    virtual ::std::string getDst(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::getDst()" << std::endl;
		return dst;
	}

    virtual void setSrc(const ::std::string& arsc, const ::Ice::Current& = ::Ice::Current())
	{
		
		std::cout << "Order::setSrc()" << std::endl;
		src = arsc;
	}

    virtual void setDst(const ::std::string& adst, const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::setDst()" << std::endl;
		dst = adst;
	}

    virtual ::Ice::Float getWeight(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::getWeight()" << std::endl;
		return weight;
	}

    virtual void setWeight(::Ice::Float wgt, const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::setWeight()" << std::endl;
		weight = wgt;
	}

    virtual ::CustomerRelationsManagement::datetime getDeadline(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::getDeadline()" << std::endl;
		return deadline;
	}

    virtual void setDeadline(const ::CustomerRelationsManagement::datetime& dt, const ::Ice::Current& = ::Ice::Current())
	{
		
		std::cout << "Order::setDeadline()" << std::endl;
		deadline = dt;
	}

    virtual ::CustomerRelationsManagement::OrderStatus getStatus(const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::getStatus()" << std::endl;
		return status;
	}

	virtual void setStatus(::CustomerRelationsManagement::OrderStatus astat, const ::Ice::Current& = ::Ice::Current())
	{
		std::cout << "Order::setStatus()" << std::endl;
		status = astat;
	}

};

class CRM_imp : public CustomerRelationsManagement::CRM
{
private:
	MYSQL &db_conn;

	::CustomerRelationsManagement::customerPrx customerProxy(customer_imp *c, const ::Ice::Current& curIce)
	{
		::CustomerRelationsManagement::customerPrx proxy;
		make_proxy(c, proxy, curIce);
		return proxy;
	}

	::CustomerRelationsManagement::orderPrx orderProxy(order_imp *c, const ::Ice::Current& curIce)
	{
		::CustomerRelationsManagement::orderPrx proxy;
		make_proxy(c, proxy, curIce);
		return proxy;
	}

public:   
	CRM_imp(MYSQL &db) : db_conn(db) { }
		
	virtual ::CustomerRelationsManagement::customerPrx 
		CreateCustomer(const ::Ice::Current& curIce= ::Ice::Current())
	{
		std::cout << "CRM::CreateCustomer()" << std::endl;
		return customerProxy(new customer_imp(db_conn), curIce);
	}

	virtual ::CustomerRelationsManagement::customerPrx 
		LoadCustomerByName(const ::std::string& name, const ::Ice::Current& curIce= ::Ice::Current())
	{
		std::cout << "CRM::LoadCustomerByName()" << std::endl;
		std::ostringstream query;
		query << "SELECT customerid FROM customers WHERE name='" << escape_string(name) << "'";

		MYSQL_RES *result; 
		mysql_fast_query(db_conn, query); 
		result = mysql_store_result(&db_conn);

		if (mysql_num_rows(result) != 0) // if we have zero throw RecordNotFound, else simply use the first one
		{
			MYSQL_ROW row = mysql_fetch_row(result);
			
			int temp;
			from_string<int>(temp, row[0], std::dec);
			mysql_free_result(result);
			return customerProxy(new customer_imp(db_conn, temp), curIce);
		}
		else
		{
			mysql_free_result(result);
			throw ::CustomerRelationsManagement::RecordNotFound(0, name);
		}
	}

	virtual ::CustomerRelationsManagement::customerPrx 
		LoadCustomerById(::Ice::Int id, const ::Ice::Current& curIce = ::Ice::Current())
	{
		std::cout << "CRM::LoadCustomerById()" << std::endl;
		int itemp = 0;
		std::string foo;
		std::ostringstream query;
		query << "SELECT customerid FROM customers WHERE customerid=" << id;
		MYSQL_RES *result; 
		mysql_fast_query(db_conn, query); 
		result = mysql_store_result(&db_conn);

		if (mysql_num_rows(result) != 0) // if we have zero throw RecordNotFound, else simply use the first one
		{
			MYSQL_ROW row = mysql_fetch_row(result);
			
			int temp;
			from_string<int>(temp, row[0], std::dec);
			mysql_free_result(result);
			return customerProxy(new customer_imp(db_conn, temp), curIce);
		}
		else
		{
			mysql_free_result(result);
			throw ::CustomerRelationsManagement::RecordNotFound(id, "");
		}
	}

	virtual ::CustomerRelationsManagement::orderPrx 
		CreateOrder(const ::Ice::Current& curIce= ::Ice::Current())
	{
		std::cout << "CRM::CreateOrder()" << std::endl;
		return orderProxy(new order_imp(db_conn), curIce);
	}

	virtual ::CustomerRelationsManagement::orderPrx 
		LoadOrderById(::Ice::Int id, const ::Ice::Current& curIce= ::Ice::Current())
	{
		std::cout << "CRM::LoadOrderById()" << std::endl;
		std::ostringstream query;
		query << "SELECT orderid FROM orders WHERE orderid=" << id;
		MYSQL_RES *result; 
		mysql_fast_query(db_conn, query); 
		result = mysql_store_result(&db_conn);

		if (mysql_num_rows(result) != 0) // if we have zero throw RecordNotFound, else simply use the first one
		{
			MYSQL_ROW row = mysql_fetch_row(result);
			
			int temp;
			from_string<int>(temp, row[0], std::dec);
			mysql_free_result(result);
			return orderProxy(new order_imp(db_conn, temp), curIce);
		}
		else
		{
			mysql_free_result(result);
			throw ::CustomerRelationsManagement::RecordNotFound(id, "");
		}
	}

	virtual ::CustomerRelationsManagement::OrderList 
		LoadOrderByCustomer(::Ice::Int id, const ::Ice::Current& curIce= ::Ice::Current())
	{
		std::cout << "CRM::LoadOrderByCustomer()" << std::endl;
		::CustomerRelationsManagement::OrderList retval; //std::vector<orderPrx>
		std::ostringstream query;
		query << "SELECT orderid FROM orders WHERE customerid=" << id;

		MYSQL_RES *result;
		mysql_fast_query(db_conn, query); 
		result = mysql_store_result(&db_conn);

		retval.reserve((size_t)mysql_num_rows(result));
		int temp;
		for (size_t i = 0; i < mysql_num_rows(result); ++i)
		{
			MYSQL_ROW row = mysql_fetch_row(result);
			
			from_string<int>(temp, row[0], std::dec);
			retval.push_back(orderProxy(new order_imp(db_conn, temp), curIce));
		}
		
		mysql_free_result(result);

		return retval;
	}

	virtual ::CustomerRelationsManagement::OrderList 
		LoadNewOrders(const ::Ice::Current& curIce = ::Ice::Current())
	{
		std::cout << "CRM::LoadNewOrders()" << std::endl;
		::CustomerRelationsManagement::OrderList retval; //std::vector<orderPrx>
		std::ostringstream query, clean_query;
		query << "SELECT neworderid,orderid FROM neworders";
		clean_query << "DELETE FROM neworders WHERE neworderid <= "; //neworderid is autoincrement so we 
									///should have everything up to and including this row in our return from fetch_query

		MYSQL_RES *result; 
		mysql_fast_query(db_conn, query); 
		result = mysql_store_result(&db_conn);

		retval.reserve((size_t)mysql_num_rows(result));
		int highid = 0;
		int temp;
		for (size_t i = 0; i < mysql_num_rows(result); ++i)
		{
			MYSQL_ROW row = mysql_fetch_row(result);

			from_string<int>(temp, row[1], std::dec);
			retval.push_back(orderProxy(new order_imp(db_conn, temp), curIce));
			
			from_string<int>(temp, row[0], std::dec);
			if (temp > highid)
				highid = temp;
		}
		mysql_free_result(result);
		clean_query << highid;

		// purge the fetched entries
		mysql_fast_query(db_conn, clean_query);

		return retval;
	}



};

#endif // _ICE_Implementation_h_